
#ifdef VERTEX_SHADER

layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec3 aDimension;

layout(std430, binding = 0) writeonly buffer visibleBuffer
{
	int uVisible[];
};

uniform mat4 uModelViewMatrix;
uniform vec3 uViewPos;

out vec3 vDimension;
out int vInstance;

void main(void)
{
	gl_Position = vec4(aPosition, 1.0);
	vDimension = aDimension;
	vInstance = gl_VertexID;

	if (all(lessThan(abs(uViewPos - aPosition), vDimension)))
	{
		// inside bbox
		uVisible[vInstance] = 1;
	}
}

#endif

//-----------------------------------------

#ifdef GSMain

#extension GL_ARB_gpu_shader5 : enable

layout(points, invocations = 3) in;
layout(triangle_strip, max_vertices = 4) out;

uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
uniform vec3 uViewPos;
uniform vec3 uViewDir;

in vec3 vDimension[1];
in int vInstance[1];

flat out int vNum;

#ifdef _DEBUG
out vec3 vNormal;
#endif

void main()
{
	vec3 edgeBasic2 = vec3(0);
	vec3 edgeBasis0 = vec3(0);
	vec3 edgeBasis1 = vec3(0);
	vec3 faceNormal = vec3(0);
	vec3 dim = vDimension[0];
	
	if (gl_InvocationID == 0)
	{
		faceNormal.x = 1.0;
		edgeBasic2.x = dim.x;
		edgeBasis0.y = dim.y;
		edgeBasis1.z = dim.z;
	}
	else if (gl_InvocationID == 1)
	{
		faceNormal.y = 1.0;
		edgeBasic2.y = dim.y;
		edgeBasis1.x = dim.x;
		edgeBasis0.z = dim.z;
	}
	else if (gl_InvocationID == 2)
	{
		faceNormal.z = 1.0;
		edgeBasic2.z = dim.z;
		edgeBasis0.x = dim.x;
		edgeBasis1.y = dim.y;
	}

	/*mat4 worldTM = mat4(1.0);*/

	vNum = vInstance[0];

	mat4 pmv = uProjectionMatrix * uModelViewMatrix;
	vec3 worldCtr = (/*worldTM * */ gl_in[0].gl_Position).xyz;
	vec3 worldNormal = /*mat3(worldTM) * */ faceNormal;

	vec3 viewDir;
	if (uProjectionMatrix[3][3] == 0.0)
	{
		viewDir = (worldCtr + /*mat3(worldTM) * */ edgeBasic2) - uViewPos;
	}
	else
	{
		viewDir = uViewDir;
	}

	float sg = sign(dot(viewDir, worldNormal)) * -1;
	
	edgeBasic2 = /*mat3(worldTM) * */(edgeBasic2)* sg;
	edgeBasis0 = /*mat3(worldTM) * */(edgeBasis0);
	edgeBasis1 = /*mat3(worldTM) * */(edgeBasis1)* sg;

#if FLIPWIND

	gl_Position = pmv * vec4(worldCtr + (edgeBasic2 - edgeBasis0 - edgeBasis1), 1);
	EmitVertex();
	gl_Position = pmv * vec4(worldCtr + (edgeBasic2 - edgeBasis0 + edgeBasis1), 1);
	EmitVertex();
	gl_Position = pmv * vec4(worldCtr + (edgeBasic2 + edgeBasis0 - edgeBasis1), 1);
	EmitVertex();
	gl_Position = pmv * vec4(worldCtr + (edgeBasic2 + edgeBasis0 + edgeBasis1), 1);
	EmitVertex();

#else

	gl_Position = pmv * vec4(worldCtr + (edgeBasic2 - edgeBasis0 - edgeBasis1), 1);
	EmitVertex();
	gl_Position = pmv * vec4(worldCtr + (edgeBasic2 + edgeBasis0 - edgeBasis1), 1);
	EmitVertex();
	gl_Position = pmv * vec4(worldCtr + (edgeBasic2 - edgeBasis0 + edgeBasis1), 1);
	EmitVertex();
	gl_Position = pmv * vec4(worldCtr + (edgeBasic2 + edgeBasis0 + edgeBasis1), 1);
	EmitVertex();

#endif
}

#endif

//-----------------------------------------

#ifdef FRAGMENT_SHADER

layout(early_fragment_tests) in;

flat in int vNum;

#ifdef _DEBUG

in vec3 vNormal;

vec3 ToRGB(int rgb)
{
	vec3 color;
	color.r = (rgb >> 16) & 0xFF;
	color.g = (rgb >> 8) & 0xFF;
	color.b = rgb & 0xFF;
	return color;
}

layout(location = 0) out vec4 fragColor;

void main(void)
{
	vec4 diffuse = vec4(ToRGB(vNum), 1.0);
	vec3 lightDir = vec3(0.0, 0.0, 1.0);
	float NDotL = max(dot(vNormal, lightDir), 0.0);
	fragColor = diffuse; //* NDotL;
}

#else

layout(std430, binding = 0) writeonly buffer visibleBuffer
{
	int uVisible[];
};

void main()
{
	uVisible[vNum] = 1;
	discard;
}

#endif

#endif

